/*************************************************************************** 
 * (c) 2006 Dmitry Osinovsky, osdmsoftware@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Resources;
using OsdmSoftware.CommonLibrary;

namespace OsdmSoftware.PocketPicture
{
    public class PencilTool: PaintTool
    {
        int oldX, oldY;
        public PencilTool()
            : base("Select pencil")
        {
            ParentName = "BrushType";
        }

        public override void SelectTool() { Form.HintLabel.Text = "Drawing with pencil"; }
        public override void MouseDown(int x, int y)
        {
            oldX = x;
            oldY = y;
            MouseMove(x, y);
        }

        public override void MouseMove(int x, int y)
        {
            Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);
            gr.DrawLine(new Pen(Form.PaletteSelectedColor), oldX, oldY, x, y);
            gr.FillRectangle(new SolidBrush(Form.PaletteSelectedColor), oldX, oldY, 1, 1);
            gr.Dispose();
            Form.BitmapZoomer.InvalidateBitmap(Math.Min(oldX, x) - 1, Math.Min(oldY, y) - 1, Math.Abs(oldX - x) + 2, Math.Abs(oldY - y) + 2);
            oldX = x;
            oldY = y;
        }

        public override void MouseUp() { Form.PushToUndo(); }
        public override void Cancel() { }
    }

    public class FillTool: PaintTool
    {
        public static Point[] Shifts = { new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1) };

        public FillTool()
            : base("Select flood fill tool")
        {
            ParentName = "Contours";
        }

        public override void SelectTool() { Form.HintLabel.Text = "Select point where to fill"; }

        struct Line
        {
            public int x1;
            public int y1;
            public int x2;
            public Line(int x1, int y1, int x2)
            {
                this.x1 = x1;
                this.x2 = x2;
                this.y1 = y1;
            }

            //public Line(int x1, int y1) : this(x1, y1, -1) { }
        }

        public override unsafe void MouseDown(int x, int y)
        {
            Bitmap bmp = Form.BitmapZoomer.Bitmap;
            Rectangle rect1 = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);

            byte* pSourceRow = (byte*) bmpData.Scan0.ToPointer();
            pSourceRow += bmpData.Stride * y;
            ColorBgra* pSourcePixel = ((ColorBgra*) pSourceRow) + x;

            Color c = (*pSourcePixel).ToColor();
            Color newColor = Form.PaletteSelectedColor;
            ColorBgra cBgra = ColorBgra.FromColor(c);
            ColorBgra newColorBgra = ColorBgra.FromColor(newColor);

            if (Utility.RGBEqual(c, newColor))
            {
                bmp.UnlockBits(bmpData);
                MessageBox.Show("Pixel is already at selected color");
                return;
            }

            Form.HintLabel.Text = "Please wait...";
            Form.HintLabel.Refresh();

            List<Line> points = new List<Line>();
            Pen pen = new Pen(newColor);
            points.Add(new Line(x, y, x));
            int counter = 0;
            while (points.Count != 0)
            {
                counter++;

                if (counter % 200 == 199)
                {
                    Form.HintLabel.Text = "Please wait... " + counter.ToString() + " lines processed";
                    Form.HintLabel.Refresh();
                }

                if (counter % 2000 == 1999)
                {
                    bmp.UnlockBits(bmpData);
                    Form.BitmapZoomer.Refresh();
                    bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
                }

                if (counter > 20000)
                {
                    bmp.UnlockBits(bmpData);
                    Form.BitmapZoomer.Refresh();
                    if (MessageBox.Show("Seems like flood fill takes too long. Maybe area is too big or some error in the algorithm. Do you want to continue?",
                        "Continue operation", MessageBoxButtons.YesNo, MessageBoxIcon.None, MessageBoxDefaultButton.Button1) != DialogResult.Yes)
                    {
                        bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
                        Form.SetMode(null);
                        break;
                    }
                    Form.BitmapZoomer.Refresh();
                    bmpData = bmp.LockBits(rect1, ImageLockMode.ReadWrite, ColorBgra.PixelFormat);
                    counter = 0;
                }

                Line p = points[0];
                points.RemoveAt(0);

                int y1 = p.y1;
                int west = p.x1 - 1;

                pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                pSourceRow += bmpData.Stride * y1;
                pSourcePixel = ((ColorBgra*) pSourceRow) + west;

                while (west >= 0 && ColorBgra.RGBEqual(*pSourcePixel, cBgra, Form.Tolerance))
                {
                    west--;
                    pSourcePixel--;
                }

                west++;

                int east;
                east = p.x2 + 1;
 
                pSourcePixel = ((ColorBgra*) pSourceRow) + east;
                while (east < bmp.Width && ColorBgra.RGBEqual(*pSourcePixel, cBgra, Form.Tolerance))
                {
                    east++;
                    pSourcePixel++;
                }

                east--;

                pSourcePixel = ((ColorBgra*) pSourceRow) + west;
                for (int xn = west; xn <= east; xn++, pSourcePixel++)
                {
                    *pSourcePixel = newColorBgra;
                }

                if (y1 > 0)
                {
                    bool included = false;
                    int startposition = -1;

                    pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                    pSourceRow += bmpData.Stride * (y1 - 1);
                    pSourcePixel = ((ColorBgra*) pSourceRow) + west;

                    for (int x1 = west; x1 <= east; x1++, pSourcePixel ++)
                    {
                        if (ColorBgra.RGBEqual(*pSourcePixel, cBgra, Form.Tolerance))
                        {
                            if (!included)
                                startposition = x1;

                            included = true;
                        }
                        else
                        {
                            if (included)
                                points.Add(new Line(startposition, y1 - 1, x1 - 1));
                            included = false;
                        }
                    }

                    if (included)
                        points.Add(new Line(startposition, y1 - 1, east));
                }

                if (y1 < bmp.Height - 1)
                {
                    bool included = false;
                    int startposition = -1;

                    pSourceRow = (byte*) bmpData.Scan0.ToPointer();
                    pSourceRow += bmpData.Stride * (y1 + 1);
                    pSourcePixel = ((ColorBgra*) pSourceRow) + west;

                    for (int x1 = west; x1 <= east; x1++, pSourcePixel ++)
                    {
                        if (ColorBgra.RGBEqual(*pSourcePixel, cBgra, Form.Tolerance))
                        {
                            if (!included)
                                startposition = x1;

                            included = true;
                        }
                        else
                        {
                            if (included)
                                points.Add(new Line(startposition, y1 + 1, x1 - 1));
                            included = false;
                        }
                    }

                    if (included)
                        points.Add(new Line(startposition, y1 + 1, east));
                }
            }

            bmp.UnlockBits(bmpData);

            Form.BitmapZoomer.Refresh();

            Form.PushToUndo();

            SelectTool();

        }

        public override void MouseMove(int x, int y) { }

        public override void MouseUp() { }
        public override void Cancel() { }
    }

    public class ColorSelectionTool: PaintTool
    {
        PaintTool oldMode;

        public ColorSelectionTool()
            : base("Color selection tool.\r\nPress once to select\r\n color from image.\r\nPress twice to enter\r\n red-green-blue values for color")
        {
            ParentName = "AdvancedColor";
        }

        public override void DoAction()
        {
            if (Form.Mode == this)
            {
                if (Form.ReturnToPreviousModeAfterColorSelectionTool)
                    Form.Mode = oldMode;
                Form.ShowColorDialog();
                //base.base.DoAction();
            }
            else
            {
                base.DoAction();
            }
        }

        public override void SelectTool()
        {
            Form.HintLabel.Text = "Point pixel from which to select color";
            oldMode = Form.Mode;
            Element.ImageIndex = Form.ColorDialogAction.ImageIndex;
            Element.Refresh();
        }

        public override void Cancel()
        {
            Element.ImageIndex = ImageIndex;
            Element.Refresh();
            base.Cancel();
        }

        public override void MouseDown(int x, int y)
        {
            Form.PaletteSelectedColor = Form.BitmapZoomer.Bitmap.GetPixel(x, y);
            if (Form.ReturnToPreviousModeAfterColorSelectionTool)
                Form.Mode = oldMode;
        }
    }


}